using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Threading;
using System.Xml.Serialization;
using System.ComponentModel;
using System.Windows.Forms;
using MetX.IO;

/*
	To resolve tokens during Gen-time or use them anywhere in the XLG.Pipeline interface.
	   For convenience sake the delimiter for a resolvable token can be 
		  (_commandname params_)
	   or
		 {commandname params}

	In some places I feel the {} syntax looks and works better, but when it can't for clarity sake, switch to (_? ?_)
	
	(_tokens xml_)
		Resolves to the entire set of tokens in XML format. 
		This would force asl unresolved values to resolve at once. Useful in xlgd files
		All sensitive values so named are encrypted
	
	(_tokens xml unsafe_)
		Resolves to the entire set of tokens in XML format (an <xlgTokens> text node).
		This would force asl unresolved values to resolve at once. Useful in xlgd files 		
		All sensitive values so named are DECRYPTED. Use with care 		    	
	
	(_tokens xsl_)
		Resolves to the entire set of tokens as a list of <xsl:variable /> nodes. 
		All sensitive values so named are encrypted
	
	(_tokens xsl unsafe_)
		Resolves to the entire set of tokens as a list of <xsl:variable /> nodes. 
		All sensitive values so named are DECRYPTED. Use with care 		    	
	
	(_tokens edit_) 	   
	   Resolves to an empty string. 	   
		  However, gen time is paused and the user is presented with the XLG Pipeline tokens editor
				with values for all tokens resolved based on curent gen-time. Changes made are effective
				only until the end of gen-time for the current step. 	 	
							
	(_tokens use filename_) 
	   Takes the supplied .xlgt file (must already exist) and uses just those resolved tokens during gen-time
			until the next step is executed
			or until another call to 
				(__) is made 	 	
	
	(_tokens normal_)	
	   Stops using any previously supplied temporary token list (if any).
	
	(_add filename_) 
	   Takes the supplied .xlgt file (must already exist) and adds tokens in that file to the current gen-time 		   	
		  
	(_set name value_)
	   Sets or Clears the value of a token

	(_name_) 
	   Simple resolve based on the As attribute (defaults to string) 		
		  If the token has an As attribute of
  	  ConnectionString, SecurityIssue, or Password 		  
			(_name_)  will return the encrypted value 		  
			(_!name_) will return the decrypted value 		
		  When a value doesn't decrypt, it is treated as if it was never entered usually asking the user what the value should be 				
		  Encrypted values are unique as follows: 			  
			  in this file: Tied to the machine 		      
			  in a xlgs file: Tied to the logged in user 			  
			  in a log file: One way MD5 hash 			   	
	(_._) 		
		Returns the path XLG.Pipeline.exe is currently running in 	 	
	
	(_.._) 	    
		Returns the parent path XLG.Pipeline.exe is currently running in (and presumably where Support is).  	
			  
	(_guid_) 		
		Returns a new Guid as a string 			   	
	
	(_folder SpecialFolderName_) 		
		Resolves to one of the values for System.Enviornment.SpecialFolders 
			  Example (_folder MyDocuments_) would resolve the logged in user's my documents folder 		

	(_env toresolve_)
		If any other value is passed, it is resolved as if passed to System.Environment.ExpandEnvironmentVariables
			  
	(_t TemplateNamePart_)
	(_template TemplateNamePart_)
		Resolves TemplateNamePart (just the part of the file without .xsl or .xlg.xsl) to a full path and filename for a template 
			  that must exist under either your "My Xlg Templates" (in your My Documents folder) 
			  or in the folder referenced in (_StandardTemplates_) (whichever comes first)
		NOTE: If this doesn't resolve, pipeline processing will be shut down.

 */
namespace MetX.Data
{
	public class xlgTokenResolver
	{
		public Form Parent;
		public xlgTokenListManager TokenListMan;
        public xlgFolder TemplateFolders;
		public xlgSource CurrentStep;

        Environment.SpecialFolder SystemFolders;

		public xlgTokenResolver(Form Parent) 
		{
			this.Parent = Parent;
			this.TokenListMan = new xlgTokenListManager();
		}

		public string Resolve(string ToResolve)
		{
			if(string.IsNullOrEmpty(ToResolve))
				return string.Empty;

			bool DidSomething;
			do
			{
				DidSomething = false;
				string CurrAction = Token.Between(ToResolve, "(_", "_)");
				if (CurrAction.Length > 0 && CurrAction.Length < 50)
				{
					string CurrParams = Token.After(CurrAction, 1);
					if (!string.IsNullOrEmpty(CurrParams))
						CurrAction = Token.First(CurrAction);
					string RD = ResolveParBar(CurrAction, CurrParams);
					ToResolve = Token.First(ToResolve, "(_") + RD + Token.After(ToResolve, 1, "_)");
					DidSomething = true;
				}
				else 
				{
					CurrAction = Token.Between(ToResolve, "{", "}");
					if (CurrAction.Length > 0 && CurrAction.Length < 50)
					{
						string CurrParams = Token.After(CurrAction, 1);
						if (!string.IsNullOrEmpty(CurrParams))
							CurrAction = Token.First(CurrAction);
						string RD = ResolveParBar(CurrAction, CurrParams);
						ToResolve = Token.First(ToResolve, "{") + RD + Token.After(ToResolve, 1, "}");
						DidSomething = true;
					}
				}
			} while (DidSomething);

			return ToResolve;
		}

        List<xlgFile> DiscoveredTemplateFiles;

        private string ResolvePound(string p)
        {
            DiscoverTemplateFiles(false);

            if (DiscoveredTemplateFiles != null && DiscoveredTemplateFiles.Count > 0)
            {
                xlgFile CurrTemplate = DiscoveredTemplateFiles.Find(delegate(xlgFile CurrFile) { return CurrFile.Name.StartsWith(p); });
                if (CurrTemplate != null)
                    return (CurrTemplate.Path.EndsWith(@"\") ? CurrTemplate.Path.Substring(0, CurrTemplate.Path.Length - 1) : CurrTemplate.Path);
            }

            return string.Empty;
        }

        private void DiscoverTemplateFiles(bool StartFromScratch)
        {
            DiscoveredTemplateFiles = null;
            if (TemplateFolders == null || StartFromScratch)
                DiscoverTemplateFoldersNow(StartFromScratch);
            if(TemplateFolders != null)
            {
                DiscoveredTemplateFiles = new List<xlgFile>();
                TemplateFolders.FindAllExtension(DiscoveredTemplateFiles, ".xsl", new string[] { "Archive", "_svn", ".svn" });
            }
        }

        private void DiscoverTemplateFoldersNow(bool StartFromScratch)
        {
            if (TemplateFolders == null || StartFromScratch)
            {
                TemplateFolders = new xlgFolder();
                TemplateFolders.Name = "DiscoveredTemplateFolders";
                xlgTokenList TemplateFolderTokens = TokenListMan.FindAllByTokenType(xlgTokenType.MyTemplatesFolder, xlgTokenType.TemplatesFolder);
                if (TemplateFolderTokens != null && TemplateFolderTokens.Tokens != null && TemplateFolderTokens.Tokens.Count > 0)
                {
                    foreach (xlgToken CurrToken in TemplateFolderTokens.Tokens)
                    {
                        if (!CurrToken.IsResolved)
                            ResolveToken(CurrToken);
                        if (!string.IsNullOrEmpty(CurrToken.Value) && Directory.Exists(CurrToken.Value))
                            FileSystem.DeepContents(TemplateFolders, new DirectoryInfo(CurrToken.Value));
                    }
                }
            }
        }

		private string ResolveAt(string p)
		{
			if(!string.IsNullOrEmpty(p))
			{
				switch(p)
				{
					case ".":
                        return Environment.CurrentDirectory;

					case "..":
						return Token.BeforeLast(Environment.CurrentDirectory, @"\");

					case "Guid":
                        return Guid.NewGuid().ToString();
					
					default:
                        if (Enum.IsDefined(typeof(Environment.SpecialFolder), p))
                            return Environment.GetFolderPath((Environment.SpecialFolder) Enum.Parse(typeof(Environment.SpecialFolder), p));
                        else
                            return Environment.GetEnvironmentVariable(p);
				}
			}
			return string.Empty;
		}

		private string ResolveBang(string p)
		{
            string ret = ResolveDollar(p);
            if (!string.IsNullOrEmpty(p) && p.StartsWith("~~") & p.EndsWith("~~"))
            {
                ret = MetX.Security.Crypt.FromBase64(p);
            }
            return ret;
		}

		public string ResolveToken(string TokenName)
		{
			xlgToken CurrToken = null; 
			foreach (xlgTokenList CurrList in TokenListMan.Lists)
			{
				CurrToken = CurrList[TokenName];
				if (CurrToken != null)
					return ResolveToken(CurrToken);
			}
			return string.Empty;
		}

		public string ResolveToken(xlgToken CurrToken)
		{
			if (CurrToken != null)
			{
				if (!CurrToken.IsResolved)
				{
					if (!string.IsNullOrEmpty(CurrToken.Default))
					{
						if (string.IsNullOrEmpty(CurrToken.Prompt))
							CurrToken.Value = Resolve(CurrToken.Default);
						else
						{
							string ResolvedDefault = Resolve(CurrToken.Default);
							CurrToken.Value = Microsoft.VisualBasic.Interaction.InputBox(CurrToken.Prompt, "What is the value of: " + CurrToken.Name, ResolvedDefault, -1, -1);
						}
					}
					else if (!string.IsNullOrEmpty(CurrToken.Prompt))
					{
						CurrToken.Value = Microsoft.VisualBasic.Interaction.InputBox(CurrToken.Prompt, "What is the value of: " + CurrToken.Name, string.Empty, -1, -1);
					}

					if(CurrToken.Value == null)
						CurrToken.Value = string.Empty;
					CurrToken.IsResolved = true;
				}
				return CurrToken.Value;
			}
			return string.Empty;
		}

		public string ResolveParBar(string Action, string Params)
		{
			if (!string.IsNullOrEmpty(Action))
			{
				switch (Action)
				{
					case "step":
					case "source":
						if (CurrentStep == null)
							return string.Empty;
						string PropertyName = Token.First(Params);
						switch (PropertyName)
						{
							case "BasePath":			return Resolve(CurrentStep.BasePath);
							case "ConnectionName":		return Resolve(CurrentStep.ConnectionName);
							case "ConnectionString":	return Resolve(CurrentStep.ConnectionString);
							case "OutputFilename":		return Resolve(CurrentStep.OutputFilename);
							case "OutputPath":			return Resolve(CurrentStep.OutputPath);
							case "OutputXml":			return Resolve(CurrentStep.OutputXml);
							case "XlgDocFilename":		return Resolve(CurrentStep.XlgDocFilename);
							case "XslFilename":			return Resolve(CurrentStep.XslFilename);
						}
						break;

					case "tokens":
						switch (Params)
						{
							case "xml":
								return TokenListMan.OuterXml();

							case "xml unsafe":
								return TokenListMan.OuterXml();
							
							case "xsl":
								StringBuilder ret = new StringBuilder();
								foreach (xlgTokenList CurrList in TokenListMan.Lists)
								{
									foreach (xlgToken CurrToken in CurrList.Tokens)
									{
										if (!CurrToken.IsResolved)
											ResolveToken(CurrToken);
										ret.Append("<xsl:variable name=\"");
										ret.Append(xml.AttributeEncode(CurrToken.Name));
										ret.Append("\">");
										ret.Append(xml.AttributeEncode(CurrToken.Value));
										ret.AppendLine("</xsl:variable>");
									}
								}
								return ret.ToString();
							
							case "xsl unsafe":
								StringBuilder ret2= new StringBuilder();
								foreach (xlgTokenList CurrList in TokenListMan.Lists)
								{
									foreach (xlgToken CurrToken in CurrList.Tokens)
									{
										if (!CurrToken.IsResolved)
											ResolveToken(CurrToken);
										ret2.Append("<xsl:variable name=\"");
										ret2.Append(xml.AttributeEncode(CurrToken.Name));
										ret2.Append("\">");
										ret2.Append(xml.AttributeEncode(CurrToken.Value));
										ret2.AppendLine("</xsl:variable>");
									}
								}
								return ret2.ToString();

							case "edit":
								// show gen-time Token editor
								return string.Empty;

							case "normal":
								// Would restore previous variable state here.
								return string.Empty;
						}
						break;

					case "add":
						// merge tokens would occur here.
						return string.Empty;

					case "set":
						string TokenName = Token.First(Params);
						Params = Token.AfterFirst(Params, " ");
						TokenListMan.SetToken(TokenName, Params);
						break;

					case ".":
                        return Environment.CurrentDirectory;

					case "..":
						return Token.BeforeLast(Environment.CurrentDirectory, @"\");

					case "guid":
                        return Guid.NewGuid().ToString();
					
					case "folder":
                        if (Enum.IsDefined(typeof(Environment.SpecialFolder), Params))
							return Environment.GetFolderPath((Environment.SpecialFolder)Enum.Parse(typeof(Environment.SpecialFolder), Params));
						return string.Empty;

					case "env":
						if(Params.Contains("%"))
							return Environment.ExpandEnvironmentVariables(Params);
						return Environment.GetEnvironmentVariable(Params);

					case "t":
					case "template":
						DiscoverTemplateFiles(false);
						if (DiscoveredTemplateFiles != null && DiscoveredTemplateFiles.Count > 0)
						{
							xlgFile CurrTemplate = DiscoveredTemplateFiles.Find(delegate(xlgFile CurrFile) { return CurrFile.Name.StartsWith(Params); });
							if (CurrTemplate != null)
								return (CurrTemplate.Path.EndsWith(@"\") ? CurrTemplate.Path.Substring(0, CurrTemplate.Path.Length - 1) : CurrTemplate.Path);
						}
						return string.Empty;

					default:
						if(Action[0] == '!')
							return ResolveToken(Action);
						else
							return ResolveToken(Action);
				}
			}
			return string.Empty;
		}

		public string ResolveDollar(string InnerText)
		{
			if(!string.IsNullOrEmpty(InnerText))
			{
				switch(InnerText[0])
				{
					case '*':
						switch (InnerText.Substring(1).ToLower())
						{
							case "xml":
								return TokenListMan.OuterXml();
							case "xml unsafe":
								return TokenListMan.OuterXml();
						}
						break;

					case '$':
						// show gen-time Token editor
						return string.Empty;

					case '=':
						break;
					
					case '+':
						break;
					
					case '-':
						break;

					default:
						return ResolveToken(InnerText);
				}
			}
			return string.Empty;
		}
	}
}
